//
// Created by lenovo on 2022/3/9.
//

#include "AnrCatcher.h"
#include "JniEntrance.h"
#include <unistd.h>
#include <pthread.h>
#include <unistd.h>
#include <syscall.h>
#include <dirent.h>
#include <cstdlib>
#include <cinttypes>
#include <fcntl.h>
#include "Logging.h"
#include "support/Support.h"

#define SIGNAL_CATCHER_THREAD_NAME "Signal Catcher"
#define SIGNAL_CATCHER_THREAD_SIGBLK 0x1000

namespace AnrChecker {

    static sigset_t oldSet;

    AnrCatcher::AnrCatcher() {
        ALOGI("===================AnrCatcher1==================");
        sigset_t set;
        sigemptyset(&set);
        sigaddset(&set, SIGQUIT);
        pthread_sigmask(SIG_UNBLOCK, &set, &oldSet);
        ALOGI("===================AnrCatcher2==================");
    }

    static int getSignalCatcherThreadId() {
        char taskDirPath[128];
        DIR *taskDir;
        long long sigblk;
        int signalCatcherTid = -1;
        int firstSignalCatcherTid = -1;
        snprintf(taskDirPath, sizeof(taskDirPath), "/proc/%d/task", getpid());
        if ((taskDir = opendir(taskDirPath)) == nullptr) {
            return -1;
        }
        struct dirent *dent;
        pid_t tid;
        while ((dent = readdir(taskDir)) != nullptr) {
            tid = atoi(dent->d_name);
            if (tid <= 0) {
                continue;
            }
            char threadName[1024];
            char commFilePath[1024];
            snprintf(commFilePath, sizeof(commFilePath), "/proc/%d/task/%d/comm", getpid(), tid);
            Support::readFileAsString(commFilePath, threadName, sizeof(threadName));

            if (strncmp(SIGNAL_CATCHER_THREAD_NAME, threadName,
                        sizeof(SIGNAL_CATCHER_THREAD_NAME) - 1) != 0) {
                continue;
            }
            if (firstSignalCatcherTid == -1) {
                firstSignalCatcherTid = tid;
            }
            sigblk = 0;
            char taskPath[128];
            snprintf(taskPath, sizeof(taskPath), "/proc/%d/status", tid);
            ScopedFileDescriptor fd(open(taskPath, O_RDONLY, 0));
            LineReader lr(fd.get());
            const char *line;
            size_t len;
            while (lr.getNextLine(&line, &len)) {
                if (1 == sscanf(line, "SigBlk: %" SCNx64, &sigblk)) {
                    break;
                }
                lr.popLine(len);
            }
            if (SIGNAL_CATCHER_THREAD_SIGBLK != sigblk) {
                continue;
            }
            signalCatcherTid = tid;
            break;
        }
        closedir(taskDir);

        if (signalCatcherTid == -1) {
            signalCatcherTid = firstSignalCatcherTid;
        }
        return signalCatcherTid;
    }

    static void sendSignalToSignalCatcher() {
        int tid = getSignalCatcherThreadId();
        syscall(SYS_tgkill, getpid(), tid, SIGQUIT);
    }

    static void *handleAnrCallback(void *arg) {
        ALOGI("===================handleAnrCallback=================");
        executeAnrCallback();
        sendSignalToSignalCatcher();
        return nullptr;
    }

    void *handleAnr(void *arg) {
        ALOGI("===================handleAnrCallback=================");
        executeAnrCallback();
        sendSignalToSignalCatcher();
        return nullptr;
    }

    static void *handleSiUserAnrCallback(void *arg) {
        ALOGI("===================handleSiUserAnrCallback=================");
        executeAnrCallback();
        sendSignalToSignalCatcher();
        return nullptr;
    }

    void AnrCatcher::onReceiving(int signal, const siginfo_t *info, void *us) {
        ALOGI("===================onReceiving signal=%d=================", signal);
        int fromPid1 = info->_si_pad[3];
        int fromPid2 = info->_si_pad[4];
        ALOGI("===================onReceiving fromPid1=%d====fromPid2=%d============", fromPid1, fromPid2);
        int myPid = getpid();
        ALOGI("===================onReceiving myPid=%d=================", myPid);
        bool fromMySelf = fromPid1 == myPid || fromPid2 == myPid;
        ALOGI("===================onReceiving fromMySelf=%d=================", fromMySelf);
        if (signal == SIGQUIT) {
            pthread_t thd;
            if (!fromMySelf) {
                pthread_create(&thd, nullptr, handleAnr, nullptr);
            } else {
                pthread_create(&thd, nullptr, handleSiUserAnrCallback, nullptr);
            }
            pthread_detach(thd);
        }
    }

    AnrCatcher::~AnrCatcher() {
        pthread_sigmask(SIG_SETMASK, &oldSet, nullptr);
    }
}

